package scene

import (
	"sanguo/node/common/GlobalConstData"
	"sanguo/node/common/aoi"
	"time"

	"sanguo/node/node_map"
	excel_table "sanguo/node/table/excel/table"
	table_Map "sanguo/node/table/excel/table/Map"
	table_StoryStageVisible "sanguo/node/table/excel/table/StoryStageVisible"

	"github.com/sniperHW/kendynet/event"
)

type SceneObject interface {
	OnEnterScene()
	OnLeaveScene()
	IsAoiObject() bool
	SetScene(*Scene)
	GetScene() *Scene
	GetID() string
	Tick()
}

type Position struct {
	X     int32
	Y     int32
	Z     int32
	Angle int32
}

type SceneType uint32

type Scene struct {
	sceneType        SceneType
	aoi_             *aoi.AOI
	objects          map[string]SceneObject
	changeAoiObjects map[aoi.AOIObject]bool
	processQueue     *event.EventQueue
	leftBottom       aoi.Position
	rightTop         aoi.Position
	defaultPosition  DefaultPosition
	storyStages      map[int64]*table_StoryStageVisible.Limit_
}

type DefaultPosition struct {
	X     int32
	Y     int32
	Z     int32
	Angle int32
}

type SceneMgr struct {
	sceneMap map[SceneType]*Scene
}

var sceneMgr SceneMgr

func (this *Scene) fixPosition(pos aoi.Position) aoi.Position {

	if pos.X < this.leftBottom.X {
		pos.X = this.leftBottom.X
	}

	if pos.Y < this.leftBottom.Y {
		pos.Y = this.leftBottom.Y
	}

	if pos.X >= this.rightTop.X {
		pos.X = this.rightTop.X - 1
	}

	if pos.Y >= this.rightTop.Y {
		pos.Y = this.rightTop.Y - 1
	}

	return pos
}

func (this *Scene) GetStoryStage(stage int64) *table_StoryStageVisible.Limit_ {
	return this.storyStages[stage]
}

func (this *Scene) PostTask(task func()) {
	this.processQueue.Post(task)
}

func (this *Scene) GetObjByID(id string) SceneObject {
	return this.objects[id]
}

func (this *Scene) StartAoi(o SceneObject) {
	if o.GetScene() == this {
		node_map.Infoln(o.GetID(), "StarAoi OK")
		aoiObj := o.(aoi.AOIObject)
		this.aoi_.Add(aoiObj)
		o.OnEnterScene()
	}
}

func (this *Scene) EnterScene(o SceneObject, pos aoi.Position) bool {
	if nil != o.GetScene() {
		return false
	}
	o.(aoi.AOIObject).SetAoiPosition(this.fixPosition(pos))
	o.SetScene(this)
	this.objects[o.GetID()] = o
	return true
}

func (this *Scene) LeaveScene(o SceneObject) bool {

	if this != o.GetScene() {
		node_map.Debugln("LeaveScene this != o.GetScene")
		return false
	}

	aoiObj := o.(aoi.AOIObject)

	if aoiObj.GetGrid() == nil {
		return true
	}

	if !this.aoi_.Remove(aoiObj) {
		node_map.Debugln("aoi remove failed")
		return false
	}

	delete(this.changeAoiObjects, aoiObj)

	o.SetScene(nil)
	delete(this.objects, o.GetID())

	return true
}

func (this *Scene) Move(o SceneObject, pos aoi.Position) bool {

	//node_map.Debugln("Move", this, o)
	if this != o.GetScene() {
		return false
	}

	if o.IsAoiObject() {
		aoiObj := o.(aoi.AOIObject)
		newPos := this.fixPosition(pos)
		//node_map.Infoln("Move --------", pos, "----->", newPos)
		oldPos := aoiObj.GetAoiPosition()
		if newPos.X != oldPos.X || newPos.Y != oldPos.Y {
			aoiObj.SetAoiPosition(this.fixPosition(pos))
			if o.(aoi.AOIObject).GetGrid() != nil {
				this.changeAoiObjects[aoiObj] = true
			}
		}
	}
	return true

}

func (this *Scene) tick() {

	for _, o := range this.objects {
		o.Tick()
	}

	for o, _ := range this.changeAoiObjects {
		this.aoi_.Update(o)
	}

	if len(this.changeAoiObjects) > 0 {
		this.changeAoiObjects = map[aoi.AOIObject]bool{}
	}
}

func GetSceneByType(tt SceneType) *Scene {
	return sceneMgr.sceneMap[tt]
}

func (this *Scene) GetViewObjs(obj aoi.AOIObject) []aoi.AOIObject {
	return this.aoi_.GetViewObjs(obj)
}

func InitScene(mapDef table_Map.Map) bool {
	leftBottom := aoi.Position{
		X: 0,
		Y: 0,
	}

	rightTop := aoi.Position{
		X: mapDef.Length * GlobalConstData.PositionRate,
		Y: mapDef.Width * GlobalConstData.PositionRate,
	}

	defaultPosition := DefaultPosition{
		X:     mapDef.DefaultPosition.X,
		Y:     mapDef.DefaultPosition.Y,
		Z:     mapDef.DefaultPosition.Z,
		Angle: mapDef.DefaultRotation,
	}

	scene1 := &Scene{
		sceneType:        SceneType(mapDef.ID),
		aoi_:             aoi.New(leftBottom, rightTop, 10),
		objects:          map[string]SceneObject{},
		changeAoiObjects: map[aoi.AOIObject]bool{},
		processQueue:     event.NewEventQueue(),
		leftBottom:       leftBottom,
		rightTop:         rightTop,
		defaultPosition:  defaultPosition,
	}

	visibleDef := table_StoryStageVisible.Table_.MapIDMap[mapDef.ID]

	if visibleDef != nil {
		scene1.storyStages = map[int64]*table_StoryStageVisible.Limit_{}
		for _, v := range visibleDef.Limit {
			item := &table_StoryStageVisible.Limit_{
				StoryStage: v.StoryStage,
				Min:        v.Min,
				Max:        v.Max,
			}
			scene1.storyStages[int64(v.StoryStage)] = item
		}
	}

	sceneMgr.sceneMap[scene1.sceneType] = scene1
	node_map.Infoln("node_map initScene", scene1, " ok \n")
	go func() {
		scene1.processQueue.Run()
	}()
	go func() {
		//每秒tick两次
		for {
			time.Sleep(time.Millisecond * 500)
			for _, s := range sceneMgr.sceneMap {
				s.processQueue.Post(s.tick)
			}
		}
	}()

	return true
}

func Init() bool {
	excel_table.Load("table/excel/table/")

	if len(table_Map.Table_.IDMap) < 1 {
		return false
	}

	for k, v := range table_Map.Table_.IDMap {
		if !InitScene(*v) {
			node_map.Infof("node_map initScene %d fail\n", k)
			//return false
		}
	}
	return true
}

func init() {
	sceneMgr = SceneMgr{
		sceneMap: map[SceneType]*Scene{},
	}
}
